package org.snlab.runtime;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import org.snlab.interfaces.LinkInf;
import org.snlab.interfaces.TaskInf;

public class Task<T> implements TaskInf {
    private int DFAId;
    private Set<String> downstreams;
    private Function<LinkInf, T> attrFn;
    private Function<T, T> transFn;
    private Function<List<T>, T> aggrFn;
    private Predicate<T> predicate;
    private DIB dib;
    private BDDEngine bddEngine;

    public Task() {
        this.downstreams = new HashSet<>();
        bddEngine = dib.getBDDEngine();
    }

    @Override
    public void setAttributeFn(Function fn) {
        this.attrFn = fn;
    }

    @Override
    public void setTransitionFn(Function fn) {
        this.transFn = fn;
    }

    public void setAggrFn(Function fn) {
        this.aggrFn = fn;
    }

    @Override
    public void setPredicate(Predicate p) {
        this.predicate = p;
    }

    private List<StateTuple<T>> merge(List<StateTuple<T>> stateTuples) {
        Map<Object, List<StateTuple<T>>> groupByState = stateTuples.stream()
                .collect(Collectors.groupingBy(StateTuple::getState));
        List<StateTuple<T>> result = new ArrayList<>();
        for (Object e : groupByState.keySet()) {
            StateTuple<T> st = new StateTuple(
                    groupByState.get(e).stream().map(StateTuple::getHs).reduce(0, this.bddEngine::or), e);
            result.add(st);
        }
        return result;
    }

    @Override
    public void process(List stateTuples) {
        List<StateTuple<T>> merged = this.merge(stateTuples);
        // TODO: map
        for (StateTuple<T> e : merged) {
            boolean result = this.predicate.test(e.getState());
            if (!result) {
                System.out.println("found error");
            }
        }
    }

    @Override
    public void setDFAId(int id) {
        this.DFAId = id;
    }

    @Override
    public void addDownstreams(String... downs) {
        // this.downstreams.addAll(Set.of(downs));
    }
}
